<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>类</title>
</head>

<body>
  <script>
    //类构造函数-实例化
    class Animal { }
    //Person构造函数
    class Person {
      constructor() {
        console.log('person ctor');
      }
    }
    //蔬菜构造函数
    class Vegetable {
      constructor() {
        this.item = 'cabbage';
      }
    }
    let a = new Animal();
    //执行new操作时会调用构造函数内部的constructor属性.
    let p = new Person(); //person ctor
    let v = new Vegetable();
    console.log(v.item); //cabbage

    class Person2 {
      constructor(name) {
        console.log(arguments.length);
        this.name = name || null;
      }
    }
    let p1 = new Person2();//0
    //类实例化时传入的参数会用作构造函数的参数
    let p2 = new Person2('sloan');//1
    console.log(p1.name, p2.name); //null 'sloan'
    //原型指针修改问题,返回的不是this对象,那么这个对象不会通过instanceof检测出与这个class有关联
    class Person3 {
      constructor(override) {
        this.foo = 'foo';
        if (override) {
          return {
            bar: 'bar'
          }
        }
      }
    }

    let p31 = new Person3(), p32 = new Person3(true);
    console.log(p31, p32, p31 instanceof Person3, p32 instanceof Person3);//Person3 {foo: 'foo'} {bar: 'bar'} true false
    function A() { }
    class B { }
    let x = A();
    //类构造函数必须使用new来调用.
    //let y = B();  Class constructor B cannot be invoked without 'new'.
    let b = new B();
    let b2 = new b.constructor();
    console.log(b2);//b的constructor函数. constructor class B
    //类作为特殊函数:有prototype和constructor构造器属性
    //类中定义的constructor不会被当作构造函数,但在创建实例时就会反转结果.
    class C { }
    console.log(C, typeof C, C.prototype, C.prototype.constructor === C); //'function', constructor f , true
    let c = new C();
    console.log(c instanceof C, c.constructor === C, c instanceof C.constructor);//true true false.
    //类作为参数传递
    let classList = [
      class {
        constructor(id) {
          this.id_ = id;
          console.log(`instance ${this.id_}`);
        }
      }
    ]
    function createInstance(classDefinition, id) {
      return new classDefinition(id);
    }
    let ins = createInstance(classList[0], 1314);// instance 1314
    //类也可以立即实例化:
    let pp = new class PP {
      constructor(x) {
        console.log(x);
      }
    }('bar');
    console.log(pp);//PP{}


    //3.1实例成员:所有成员都不会在原型上共享:
    class DD {
      constructor() {
        this.name = new String('Java');
        this.sayName = () => console.log(this.name);
        this.nicknames = ['Spring', 'MVC'];
      }
    }
    let d1 = new DD(), d2 = new DD();
    d1.sayName(); //String {'Java'}
    d2.sayName();//String{'Java'}
    //每个实例成员不会在原型上共享.
    console.log(d1.name === d2.name, d1.sayName === d2.sayName, d1.nicknames === d2.nicknames);//false
    d1.name = d1.nicknames[0];
    d2.name = d2.nicknames[1];
    d1.sayName(); //Spring
    d2.sayName(); //MVC

    //3.2原型方法与访问器
    class EE {
      constructor() {
        //添加到this的所有内容都会存在于不同的实例上.
        this.locate = () => console.log('instance');
      }
      //在类中定义的所有的内容都会定义在类的原型上.
      locate() {
        console.log('prototype');
      }
    }
    let ee = new EE();
    ee.locate();//instance
    EE.prototype.locate();//prototype. locate()会定义在原型上.
    //类方法是原型上的方法,可以使用字符串,符号和计算值作为键名:
    const symbolKey = Symbol('symbolkey');
    class FF {
      stringKey() {
        console.log('invoked stringKey');
      }
      [symbolKey]() {
        console.log('invoked symbolKey');
      }
      ['computed' + 'Key']() {
        console.log('invoked computedKey');
      }
      //get和set
      set name(newName) {
        this.name_ = newName;
      }
      get name() {
        return this.name_;
      }
    }
    let ff = new FF();
    ff.name = 'Sloan';
    console.log(ff.name);//Sloan
    ff.stringKey();//invoked stringKey
    ff[symbolKey]();//invoked symbolKey
    ff.computedKey();//invoked computedKey
    //3.3静态类方法:静态类方法适合作为实例工厂:
    class GG {
      constructor(age) {
        this.age_ = age;
      }
      sayAge() {
        console.log(this.age_);
      }
      //获取一个随机年龄
      static create() {
        return new GG(Math.floor(Math.random() * 100));
      }
    }
    console.log(GG.create());
    //3.4非函数原型和类成员
    class HH {
      //实例可以进行访问的方法.
      constructor() {
        this.sex = 22;
      }
      //原型上定义的方法.
      sayName() {
        console.log(`${HH.greating} ${this.name}`);
      }
    }
    //在类上定义数据成员,相当于在static上定义
    HH.greating = 'My name is';
    //在原型上定义数据成员,相当于在class的{}中定义的都会作为原型方法和数据.
    HH.prototype.name = 'Java';
    let h = new HH();
    h.sayName();//My name is Java.
    console.log(h.sex);//22

    //3.5迭代器与生成器方法
    class II {
      constructor() {
        this.colors = ['blue', 'black', 'yellow'];
      }
      //在原型上定义生成器方法
      *createNicknameIterator() {
        yield 'Angular';
        yield 'React';
        yield 'Vue'
      }
      //在类上定义生成器方法
      static *createJobIterator() {
        yield 'Butter';
        yield 'Baker';
        yield 'China';
      }
      //添加默认的迭代器
      *[Symbol.iterator]() {
        yield* this.colors.entries();
        // return this.colors.entries(); 也可以只返回迭代器的实例.
      }
    }
    //调用在类上定义的静态生成器方法.
    let jobIter = II.createJobIterator();
    console.log(jobIter.next().value, jobIter.next().value, jobIter.next().value);//Butter Baker China
    //调用定义在原型上的生成器方法,用实例来调用
    let i = new II();
    let nickname = i.createNicknameIterator();
    console.log(nickname.next().value, nickname.next().value, nickname.next().value);//Angular React Vue
    //调用默认的迭代器方法
    for (let [idx, colors] of i) {
      console.log(colors);//blue black yellow
    }

    //3.6继承
    //继承类
    class Vehicle {
      identifyPrototype(id) {
        console.log(id, this);
      }
      static identifyClass(id) {
        console.log(id, this);
      }
    }
    class Bus extends Vehicle { };

    let vehicle = new Vehicle();
    let bus = new Bus();

    vehicle.identifyPrototype('vehicle');
    bus.identifyPrototype('bus');

    Vehicle.identifyClass('vehicle');
    Bus.identifyClass('bus');

    console.log(bus instanceof Bus, bus instanceof Vehicle); //true true
    //继承普通的构造函数
    function Vehicle2() { };
    class Bus2 extends Vehicle2 { };
    let bus2 = new Bus2();
    console.log(bus2 instanceof Bus2, bus2 instanceof Vehicle2); //true true
    //super继承
    class JJ {
      constructor(name, age) {
        this.hasEngine = true;
        this.name = name;
        this.age = age;
      }
      static identify() {
        console.log('vehicle');
      }
    }
    class JJSon extends JJ {
      constructor(name, age) {
        super(name, age); //相当于super.constructor();手动传参.
        console.log(this instanceof JJ);//true
        console.log(this);
      }
      static identify() {
        super.identify(); //继承父类上的静态方法
      }
    }
    new JJSon('Sloan', 22);//new调用,会执行constructor构造函数.
    //没有在子类中调用constructor,默认会自己调用super(),如果定义了constructor,要么调用super(),要么返回一个对象.
    class KK { }
    class KKSon1 extends KK { }
    class KKSon2 extends KK {
      constructor() {
        //调用super()
        super();
      }
    }
    class KKSon3 extends HH {
      constructor() {
        //返回一个对象
        return {};
      }
    }
    console.log(new KKSon1(), new KKSon2(), new KKSon3());

    //抽象基类,在构造器中进行判断,如果抽象基类被实例化,则抛出错误.
    class LL {
      constructor() {
        if (new.target === LL) {
          throw new Error('LL connot be directly instantiated');
        }
        if (!this.foo) {
          throw new Error('Inheriting class must define foo()');
        }
        console.log('success!');
      }
    }

    class LLSon extends LL {
      foo() { }
    }
    class LLSon2 extends LL { }
    new LLSon();
    // new LLSon2(); error.
    // new LL();  error.

    // 洗牌算法
    class SuperArray extends Array {
      shuffle() {
        for (let i = this.length - 1; i > 0; i--) {
          const j = Math.floor(Math.random() * (i + 1));
          //数据交换
          [this[i], this[j]] = [this[j], this[i]];
        }
      }
    }
    let xipai = new SuperArray(1, 2, 3, 4, 5);
    console.log(xipai instanceof Array, xipai instanceof SuperArray, xipai);
    xipai.shuffle();
    console.log(xipai);

    //类的混入
    class MM { }
    let FooMixin = (Superclass) => class extends Superclass {
      foo() {
        console.log('foo');
      }
    };
    let BarMixin = (Superclass) => class extends Superclass {
      bar() {
        console.log('bar');
      }
    };
    let BazMixin = (Superclass) => class extends Superclass {
      baz() {
        console.log('baz');
      }
    }
    // class Buss extends FooMixin(BarMixin(BazMixin(MM))) { }
    //通过写一个辅助函数,可以把嵌套调用给展开
    function mix(BaseClass, ...Mixins) {
      return Mixins.reduce((accumulator, current) => current(accumulator), BaseClass);
    }
    class Buss extends mix(MM, FooMixin, BarMixin, BazMixin) { }
    let bu = new Buss();
    bu.foo();
    bu.bar();
    bu.baz();
  </script>
</body>

</html>